package pATT.core;

import java.io.File;
import java.io.IOException;
import java.util.Enumeration;
import java.util.Vector;

import pATT.core.converters.ArffLoader;
import pATT.core.visual.UserProfilingUI;
import pATT.filters.Filter;
import pATT.filters.unsupervised.attribute.Add;
import pATT.filters.unsupervised.attribute.AddNoise;
import pATT.filters.unsupervised.attribute.Copy;
import pATT.filters.unsupervised.attribute.MergeTwoValues;
import pATT.filters.unsupervised.attribute.Obfuscate;
import pATT.filters.unsupervised.attribute.Remove;
import pATT.filters.unsupervised.attribute.ReplaceMissingValues;
import pATT.filters.unsupervised.instance.RemovePercentage;
import pATT.filters.unsupervised.instance.RemoveRange;
import pATT.logger.Logger;
import pATT.logger.Message;

public class InstanceManager {
	
	private Vector<Filter> addedFilters = new Vector<Filter>();//Todos los filtros agregados a la relacion
	private Vector addedFiltersNames = new Vector();//Todos los nombres de los filtros agregados a la relacion(para facilitar la busqueda)
	
	private Vector<Filter> allFilters = new Vector<Filter>();//Son Todos los filtros posibles
	public Instances original = null;
	public Instances inUseAux = null;
	public Instances inUse = null;
	private ArffLoader loader = null;
	/**
	 * Representa el archivo NO arff levantado
	 */
	private File fileLoaded = null;
//	private String EXTENSION = "";
//	private String nameRelation = "";
//	private int numInstances = -1;
//	private int numAttributes = -1;
	
	public static InstanceManager eINSTANCE = new InstanceManager();
	
	private InstanceManager() {
		
//		TODO Estos filtros andan joya		
		allFilters.addElement(new Add());
		allFilters.addElement(new AddNoise());
		allFilters.addElement(new Copy());
		allFilters.addElement(new MergeTwoValues());
		allFilters.addElement(new Obfuscate());
		allFilters.addElement(new Remove());
		allFilters.addElement(new RemovePercentage());
		allFilters.addElement(new RemoveRange());
		allFilters.addElement(new ReplaceMissingValues());
		
	}
	
	/**
	 * Retorna si se cargo el archivo arff*/
	public boolean hasInstances(){
		return (loader!=null);
	}
	/**
	 * Retorna si se cargo un archivo que no es arff
	 * @return
	 */
	public boolean hasFile(){
		
		return (loader == null && fileLoaded != null);
		
	}
	
	public void recuperarInstances(){
		inUse = new Instances(inUseAux);
	}
	public Vector<Filter> allFilters(){
		return allFilters;
	}
	/**
	 * Retorna la instancia del filtro dado el nombre
	 * @param name String
	 * 
	 * @return Filter 
	 * */
	public Filter getFilter(String name){
		for (int i = 0; i < allFilters.size();i++) {
			if(allFilters.get(i).getName().equals(name))
				return allFilters.get(i);		
		}
		return null;
	}
	
	
	/**
	 * Retorna el Archivo Arff que actualmente se esta utilizando
	 * 
	 * @return String
	 * */
	public String getArffFile(){
			try {
				return getLoader().getComments()+inUse.toString();
			} catch (IOException e) {
				e.printStackTrace();
			}
		return "";
	}
	/**
	 * Actualiza las instancias 
	 * 
	 * @param instances
	 * */
	public void updateInstances(Vector<Vector> vector){
		for (int i = 0; i < inUse.numInstances(); i++) {
			Instance instance = inUse.instance(i);
			int pos = 1;
			for (int j = 0; j < instance.numValues(); j++) {
				if(vector.get(i).get(pos).toString().equals("?"))
					instance.setMissing(j);
				else
					instance.setValue(j, vector.get(i).get(pos).toString());
				pos++;
			}
		}
		//TODO actualizar las nuevas instancias getLoader().setInstances(loader.getDataSet());
	}
	
	/**
	 * Retorna un vector con los atributos del dataSets 
	 * */
	public Vector<Attribute> getAttributes(){
		Vector<Attribute> attributes = new Vector<Attribute>();
		int max = numAttributes();
		for (int i = 0; i < max; i++) {
			attributes.addElement(getAttribute(i));
		}
		return attributes; 
	}


	/**Crea las instancias a partir de un archivo con extension .arff
	 * @throws IOException 
	 * 
	 * */
	public void createIntancesFromArff(File file) throws IOException{
		
//		try {
			getLoader().setSource(file);
//		} catch (IOException e) {
//			e.printStackTrace();
//		} catch (Exception e) {
//			e.printStackTrace();
//		}
//		EXTENSION = ImageFilter.ARFF_EXTENSION;
		//Instances instances = null;
//		try {
			original = new Instances(getLoader().getDataSet());
			inUse = new Instances(getLoader().getDataSet());
//			nameRelation = getLoader().getStructure().relationName();
			//	numInstances() = getLoader().getDataSet().numInstances();
			//	numAttributes() = getLoader().getStructure().numAttributes();
			//instances = getLoader().getDataSet();
//		} catch (IOException e) {
//			e.printStackTrace();
//		}
		
		Logger.log(Message.OPEN_FILE+nameRelation()+"("+numInstances()+")");
		
		UserProfilingUI.eINSTANCE.updateRelationInfo(nameRelation(), numInstances(), numAttributes());		
		UserProfilingUI.eINSTANCE.updateAttributesInfo(inUse);
	}
	
	public ArffLoader getLoader() {
		if(loader == null){
			loader = new ArffLoader();
			fileLoaded = null;
		}
		return loader;
	}
	
	public void setLoader(ArffLoader loader) {
		this.loader = loader;
	}
	
	@SuppressWarnings("unchecked")
	public void addFilterOfRelation(Filter filter){
		addedFilters.addElement(filter);
		addedFiltersNames.addElement(filter.getName());
		Logger.log(Message.ADD_FILTER+filter.getName());
	}	
	
	public boolean removeFilterOfRelation(Filter filter){
		Logger.log(Message.DELETE_FILTER+filter.getName());
		addedFiltersNames.removeElement(filter.getName());
		return addedFilters.removeElement(filter);
	}
	/**
	 * Aplica los filtros agregados hasta el momento a las instancias en uso
	 * @throws Exception 
	 * */
	public void applyFilters() throws Exception{
		inUseAux = new Instances(inUse);
		for (int i = 0; i < addedFilters.size(); i++) {
			Filter filter = addedFilters.elementAt(i);
			applyFilter(filter);
			
		}
		
		Logger.log(Message.APPLY_FILTERS);
		removeAllFiltersOfRelation();
		//TODO actualizar la pantalla del preproceso
		UserProfilingUI.eINSTANCE.updateRelationInfo(nameRelation(), numInstances(), numAttributes());		
		UserProfilingUI.eINSTANCE.updateAttributesInfo(inUse);

	}
	
	
	
	
	/**
	 * Aplica un filtro dado
	 * @throws Exception 
	 * @throws Exception 
	 */
	private void applyFilter(Filter filter) throws Exception  {
		
		  Instances copy = new Instances(inUse);
	      copy.setClassIndex(-1);
	      Instances newInstances = null;
	      filter.setInputFormat(copy);
	      newInstances = Filter.useFilter(copy, filter);
	      if (newInstances == null || newInstances.numAttributes() < 1) {
				throw new Exception("Dataset is empty.");
	      }
    	  inUse = new Instances(newInstances);
    	  //TODO se estan inicializando las variables del filtros
    	  filter.cleanFields();

//		
		
		
	}
	public Instance getInstance(int pos){
		return inUse.instance(pos);
	}
	
	/**
	 * Retorna los valores de un atributo en un vector
	 * */
	@SuppressWarnings("unchecked")
	public Vector getVectorValuesAttribute(Attribute attribute){
		Vector values = new Vector();
		Enumeration enumeration = attribute.enumerateValues();
		if(enumeration == null){
			System.out.print("");
		}
		while (enumeration.hasMoreElements()) {
			values.addElement(enumeration.nextElement());			
		}
		return values;
		
	}
	
	public Attribute getAttribute(int pos){
		return inUse.attribute(pos);		
	}
	
	public Attribute getAttribute(String name){
		return inUse.attribute(name);
	}
	
	public AttributeStats getAttributeStats(int pos){
		return inUse.attributeStats(pos);
	}
	
	public String nameRelation() {
		return inUse.relationName();
	}
	
	public int numAttributes() {
		return inUse.numAttributes();
	}
	
	public int numInstances() {
		return inUse.numInstances();
	}
	public void removeAllFiltersOfRelation() {
		addedFilters.removeAllElements();
		addedFiltersNames.removeAllElements();
		
	}
	/**
	 * Retorna el vector con los nombres de los filtros agregado a la relacion
	 * */
	public Vector getAddedFilter(){
		return addedFiltersNames;
	}
	/**
	 * Retorna si ya se agrego el filtro a la relacion
	 * @return boolean
	 * */
	public boolean exists(String name) {
		
		return addedFiltersNames.contains(name);
	}
	public boolean isEmptyAddedFilters() {
		return addedFilters.isEmpty();
	}
	public boolean isFilter(String nameFilter) {
		for (int i = 0; i < allFilters.size(); i++) {
			if(allFilters.elementAt(i).getName().equals(nameFilter))
				return true;
		}
		return false;
	}

	public File getFileLoaded() {
		return fileLoaded;
	}

	public void setFileLoaded(File fileLoaded) {
		this.fileLoaded = fileLoaded;
		loader = null;
	}

	
}
